use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

use crate::client::TongLianPayClient;
use crate::utils;

#[derive(Debug, Serialize)]
pub struct TradeParams {
    pub trxamt: i32,
    pub reqsn: String,
    pub paytype: String,
    pub body: String,
    pub acct: String,
    pub notify_url: String,
    pub limit_pay: String,
}

/// weixin JSAPI 下单时，针对返回的 prepay_id 生成的签名，
/// 前端在调起微信支付时，需要这些参数。
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TradeSignature {
    pub app_id: String,
    #[serde(rename = "timeStamp")]
    pub timestamp: String, // 注意，单位为秒。类型为 string。
    pub nonce_str: String,
    // 须形如 `prepay_id=xxxxx`。注意 xxxx 前后无引号。
    pub package: String,
    // 统一为 RSA
    pub sign_type: String,
    pub pay_sign: String,
}

impl TongLianPayClient {
    pub async fn prepay(&self, params: &TradeParams) -> Result<TradeSignature> {
        let mut body = BTreeMap::new();
        body.insert("cusid", self.mch_credential.cusid.as_str());
        body.insert("appid", self.mch_credential.appid.as_str());
        body.insert("sub_appid", self.mch_credential.sub_appid.as_str());
        body.insert("signtype", self.mch_credential.signtype.as_str());

        let trxamt = format!("{}", params.trxamt);
        body.insert("trxamt", trxamt.as_str());

        body.insert("reqsn", params.reqsn.as_str());
        body.insert("paytype", params.paytype.as_str());

        let randomstr = utils::generate_none_str(32);
        body.insert("randomstr", randomstr.as_str());

        body.insert("body", params.body.as_str());
        body.insert("acct", params.acct.as_str());
        body.insert("notify_url", params.notify_url.as_str());
        body.insert("limit_pay", params.limit_pay.as_str());

        // 加签
        let plain = utils::btree_map_to_query_string(&body);
        let sign = self.mch_credential.sign(plain.as_str());
        body.insert("sign", &sign);

        let url = format!("{}/apiweb/unitorder/pay", self.base_url);
        let response = self
            .client
            .post(&url)
            .form(&body)
            .header("Accept", "application/json")
            .send()
            .await?;

        if !response.status().is_success() {
            bail!("支付下单失败");
        }

        let mut response: BTreeMap<String, String> = response.json().await?;
        // check response has sign key
        if !response.contains_key("sign") {
            let msg = response
                .get("retmsg")
                .unwrap_or(&"支付下单失败".to_string())
                .to_string();

            bail!(msg);
        }

        // 验签
        self.mch_credential.verify_response(&mut response)?;

        let trxstatus = response.get("trxstatus").unwrap();
        if trxstatus != "0000" {
            if let Some(errmsg) = response.get("errmsg") {
                bail!("{},status code: {}", errmsg, trxstatus);
            } else {
                bail!("status code:{}", trxstatus);
            }
        }
        let payinfo = response.get("payinfo").unwrap();
        Ok(serde_json::from_str(payinfo)?)
    }
}
